package com.scheduler.servicediscover;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.util.List;
import java.util.Map;

@Slf4j
public class MysqlServiceDiscoverRegistrationCenter implements ServiceDiscoverRegistrationCenter {
    /**
     * 数据源的名称，多数据源下使用，可以单独指定一个 bean，也可以指定一个别名即可
     */
    private static final String[] DATA_SOURCE_BEAN_NAME = new String[]{"dataSourceMa", "dataSource"};

    private static final String CREATE_TABLE =
            "CREATE TABLE IF NOT EXISTS `marketing_server_discover` (\n" +
                    "  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',\n" +
                    "  `ip_addr` varchar(128) DEFAULT NULL COMMENT 'IP地址，可能包含协议、端口等',\n" +
                    "  `server_group` varchar(64) DEFAULT 'default' COMMENT '服务组',\n" +
                    "  `gmt_create` datetime(3) DEFAULT NULL COMMENT '创建时间',\n" +
                    "  `gmt_modified` datetime(3) DEFAULT NULL COMMENT '修改时间，心跳上报的时候会更新',\n" +
                    "  PRIMARY KEY (`id`),\n" +
                    "  UNIQUE KEY `uk_ip_addr` (`ip_addr`,`server_group`) USING BTREE COMMENT '唯一键'\n" +
                    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";

    private static final String HEART_BEAT = "INSERT INTO marketing_server_discover (ip_addr, server_group,gmt_create, gmt_modified) VALUE('%s','%s',now(3),now(3)) ON DUPLICATE KEY UPDATE ip_addr = '%s',server_group = '%s',gmt_modified = now(3);";

    private static final String ACTIVE_SERVER_LIST = "SELECT ip_addr FROM marketing_server_discover WHERE server_group='%s' and gmt_modified > DATE_ADD(now(3),INTERVAL - 10 SECOND);";

    private final ApplicationContext applicationContext;

    private JdbcTemplate jdbcTemplate;

    /**
     * 设置服务组
     */
    private final String serverGroup;

    public MysqlServiceDiscoverRegistrationCenter(ApplicationContext applicationContext, String serverGroup) {
        this.applicationContext = applicationContext;
        this.serverGroup = serverGroup;
    }

    @Override
    public void init() {
        // 加字段和索引（由于 TDDL 不能建表，此处忽略创建失败的情况）
        // runIgnoreException(() -> getJdbcTemplate().execute(CREATE_TABLE));
    }

    private DataSource getDataSourceBean() {
        Map<String, DataSource> beans = applicationContext.getBeansOfType(DataSource.class);
        if (beans.size() == 0) {
            throw new NoSuchBeanDefinitionException(DataSource.class);
        }
        if (beans.size() == 1) {
            return beans.values().iterator().next();
        }
        // 多数据源则取默认的
        for (String dataSourceName : DATA_SOURCE_BEAN_NAME) {
            DataSource dataSource = beans.get(dataSourceName);
            if (dataSource != null) {
                return dataSource;
            }
        }
        // 如果取不到则取第一个
        return beans.values().iterator().next();
    }

    private synchronized JdbcTemplate getJdbcTemplate() {
        if (jdbcTemplate == null) {
            jdbcTemplate = new JdbcTemplate(getDataSourceBean());
        }
        return jdbcTemplate;
    }

    @Override
    public void doHeartbeat(String ip) {
        String sql = String.format(HEART_BEAT, ip,serverGroup, ip,serverGroup);
        getJdbcTemplate().execute(sql);
    }

    @Override
    public List<Worker> getAllWorkers() {
        String sql = String.format(ACTIVE_SERVER_LIST,serverGroup);
        List<Worker> serverList = getJdbcTemplate().query(sql, new BeanPropertyRowMapper<>(Worker.class));
        return serverList;
    }

    private void runIgnoreException(Runnable runnable) {
        try {
            runnable.run();
        } catch (Throwable ignore) {
            log.warn("自动建表失败，可能你需要手动创建表，表名：marketing_server_discover");
        }
    }
}
